package com.sz.common.core.service.scheduler;

import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Function: TODO: ADD FUNCTION <br>
 * Author: Charles. <br>
 * Date: 2017-04-08 13:56:00.
 */
@Configuration
@ConditionalOnProperty(prefix = "org.quartz.scheduler", value = "enabled")
public class QuartzFactory {

    @Autowired
    QuartzConfig quartzConfig;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        // this allows to update triggers in DB when updating settings in config file:
        //用于quartz集群,QuartzScheduler 启动时更新己存在的Job，这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);
        //用于quartz集群,加载quartz数据源
        //QuartzScheduler 延时启动，应用启动完10秒后 QuartzScheduler 再启动
        factory.setStartupDelay(10);
        //用于quartz集群,加载quartz数据源配置
        factory.setQuartzProperties(quartzConfig.quartzProperties());
        factory.setAutoStartup(true);
        factory.setApplicationContextSchedulerContextKey("applicationContext");
        //注册触发器
        Trigger[] cronJobTriggers = createTriggers();

        if (cronJobTriggers != null && cronJobTriggers.length > 0) {
            factory.setTriggers(cronJobTriggers);
        }
        //直接使用配置文件
        return factory;
    }

    /**
     * 创始触发器
     *
     * @return
     */
    protected Trigger[] createTriggers() {
        List<QuartzConfig.Task> tasks = quartzConfig.getTasks();
        List<Trigger> triggers = new ArrayList<>();
        if (tasks != null && tasks.size() > 0) {
            for (QuartzConfig.Task task : tasks) {
                if (task.isEnabled()) {
                    String beanName = task.getBean();
                    String express = task.getExpress();
                    if (!StringUtils.hasLength(beanName) || !StringUtils.hasLength(express)) {
                        throw new IllegalArgumentException("scheduler task or express cannot be empty");
                    }
                    JobDetailFactoryBean factoryBean = createJobDetail(InvokingJobDetailDetailFactory.class, "defaultGroup", beanName);
                    CronTriggerFactoryBean  cronTriggerFactoryBean = createJobTrigger(factoryBean.getObject(), express);
                    triggers.add(cronTriggerFactoryBean.getObject());
                }
            }
        }
        return triggers.toArray(new Trigger[0]);
    }

    /**
     * 创建job工厂
     *
     * @param jobClass
     * @param groupName
     * @param targetObject
     * @return
     */
    private static JobDetailFactoryBean createJobDetail(Class<?> jobClass, String groupName, String targetObject) {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(jobClass);
        factoryBean.setName(targetObject);
        factoryBean.setDurability(true);
        factoryBean.setRequestsRecovery(false);
        factoryBean.setGroup(groupName);
        Map<String, String> map = new HashMap<>();
        map.put("targetObject", targetObject);
        map.put("targetMethod", "execute");
        map.put("concurrent", "false");
        factoryBean.setJobDataAsMap(map);
        factoryBean.afterPropertiesSet();
        return factoryBean;
    }

    /**
     * 创建触发器工厂
     *
     * @param jobDetail
     * @param cronExpression
     * @return
     */
    private static CronTriggerFactoryBean createJobTrigger(JobDetail jobDetail, String cronExpression) {
        CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
        factoryBean.setName(jobDetail.getKey().getName());
        factoryBean.setJobDetail(jobDetail);
        factoryBean.setCronExpression(cronExpression);
        try {
            factoryBean.afterPropertiesSet();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return factoryBean;
    }
}
